package cn.mazexal.examapi.models.thirdpart.huawei;

import cn.hutool.core.codec.Base64;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.http.util.TextUtils;

import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.*;

/**
 * 备注
 *
 * @author Lay
 * @date 2020/3/16
 */
public class HuaweiSignUtil {
    /**
     * 根据参数map获取待签名字符串
     * @param params 待签名参数map
     * @param includeEmptyParam 是否包含值为空的参数：
     *                          与HMS-SDK支付能力交互的签名或验签，需要为false（不包含空参数）
     *                          由华为支付服务器回调给开发者的服务器的支付结果验签，需要为true（包含空参数）
     * @return 待签名字符串
     */
    private static String getNoSign(Map<String, Object> params, boolean includeEmptyParam) {
        StringBuilder content = new StringBuilder();
        // 按照key做排序
        List<String> keys = new ArrayList<>(params.keySet());
        Collections.sort(keys);
        String value = null;
        Object object = null;
        boolean isFirstParam = true;
        for (int i = 0; i < keys.size(); i++) {
            String key = keys.get(i);
            object = params.get(key);

            if (object == null) {
                value = "";
            }else if (object instanceof String) {
                value = (String) object;
            } else {
                value = String.valueOf(object);
            }
            // 拼接成key=value&key=value&....格式的字符串
            if (includeEmptyParam || !TextUtils.isEmpty(value)) {
                content.append((isFirstParam ? "" : "&") + key + "=" + value);
                isFirstParam = false;
            } else {
                continue;
            }
        }
        //待签名的字符串
        return content.toString();
    }

    /**
     * 签名字符串（SHA256WithRSA）
     * @param content 待签名字符串
     * @param privateKey 私钥
     * @return 字符串的签名
     */
    public static String rsaSign(String content, String privateKey) {
        if (null == content || privateKey == null) {
            return null;
        }
        try {
            PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(Base64.decode(privateKey));
            KeyFactory keyf = KeyFactory.getInstance("RSA");
            PrivateKey priKey = keyf.generatePrivate(priPKCS8);
            java.security.Signature signature = java.security.Signature.getInstance("SHA256WithRSA");
            signature.initSign(priKey);
            signature.update(content.getBytes("UTF-8"));
            byte[] signed = signature.sign();
            // 使用BASE64进行编码
            return Base64.encode(signed);
        } catch (Exception e) {
            //  这里是安全算法，为避免出现异常时泄露加密信息，这里不打印具体日志
            throw new RuntimeException(e);
        }
    }

    public static String signPayReq(HuaweiPayReq payReq, String privateKey) {
        Map<String, Object> toSignMap = JSONObject.parseObject(JSON.toJSONString(payReq));
        toSignMap.remove("serviceCatalog");
        toSignMap.remove("merchantName");
        toSignMap.remove("extReserved");
        return rsaSign(getNoSign(toSignMap, false), privateKey);
    }

    /**
     * 校验签名信息
     * @param params
     * @param publicKey
     * @return
     */
    public static boolean checkSign(Map<String, Object> params, String publicKey) {
        try {
            String signType = Optional.ofNullable(params.get("signType")).map(t -> t.toString()).orElse("RSA");
            String sign = params.get("sign").toString();
            params.remove("signType");
            params.remove("sign");
            String content = getNoSign(params, true);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            byte[] encodedKey = Base64.decode(publicKey);
            PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
            java.security.Signature signature = null;
            if ("RSA256".equals(signType)) {
                signature = java.security.Signature.getInstance("SHA256WithRSA");
            } else {
                signature = java.security.Signature.getInstance("SHA1WithRSA");
            }

            signature.initVerify(pubKey);
            signature.update(content.getBytes("utf-8"));
            return signature.verify(Base64.decode(sign));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }
}

